#include <rtthread.h>
#include <string.h>
#include "board.h"
#include "st_crc16.h"
#include <tiny_md5.h>
#include "st_filesystem.h"
#include <dfs_posix.h>
#include "cJSON.h"
#include "st_tcpclient.h"
#include "easyflash.h"
#include "st_upgrade.h"


#if !defined(SAL_USING_POSIX)
#error "Please enable SAL_USING_POSIX!"
#else
#include <sys/time.h>
#include <sys/select.h>
#endif
#include <sys/socket.h> /* 使用BSD socket，需要包含socket.h头文件 */
#include "netdb.h"


#define DEBUG_TCP_CLIENT

#define DBG_TAG               "TCP"
#ifdef DEBUG_TCP_CLIENT
#define DBG_LVL               DBG_LOG
#else
#define DBG_LVL               DBG_INFO /* DBG_ERROR */
#endif
#include <rtdbg.h>

typedef enum {
    OTA_INIT = 0,
    OTA_CONNECT,
    OTA_VERSION,
    OTA_DATA,
	OTA_START,
    OTA_RREPLICATION,
    OTA_UPSUCCEED,
    OTA_STATICUP,
    OTA_REBOOT,
    OTA_HEART,
    OTA_END,
} OTA_STATUS;

typedef enum {
    OTA_REQUEST_CONNECT = 1,        //DTU请求连接
    OTA_REQUEST_VERSION,            //dtu建立链接后，设备请求更新，上报信息
    OTA_REQUEST_DATA,               //请求数据（分包循环）
    OTA_REQUEST_START,              //开始烧录
    OTA_REQUEST_UPGRADE_SUCCEED,            //
    OTA_RESPON_STATIC_UPGRADE,      //web请求静态升级
    OTA_RESPON_REBOOT,              //重启设备
    OTA_RESPON_HEART,               //心跳回复
} OTA_FRAME_TYPE;

#define FRAME_HEAR 0xac  //帧头
#define FRAME_HEAR_LENGTH 3  //帧头长度3
#define FRAME_END  0xca  //帧尾
#define FRAME_END_LENGTH 3   //帧尾长度3
#define FRAME_VERSION "1.0"  //帧版本
#define FRAME_VERSION_LENGTH 3  //帧版本长度
#define FRAME_TYPE_LENGTH 1  //帧类型长度
#define FRAME_DEVICE_TYPE_LENGTH 1  //模组型号
#define FRAME_DATA_LENGTH 2  // 帧表示数据段长度
#define FRAME_CRC16_LENGTH 2 // 帧CRC校验长度
#define FRAME_RESERVE_LENGTH 5 // 帧预留字段长度
#define FRAME_DATA_REST_LENGTH 5120
#define FRAME_EMPTY_LENGTH (FRAME_HEAR_LENGTH+FRAME_VERSION_LENGTH+FRAME_TYPE_LENGTH+FRAME_RESERVE_LENGTH+FRAME_DEVICE_TYPE_LENGTH+FRAME_DATA_LENGTH+FRAME_CRC16_LENGTH+FRAME_END_LENGTH)

#pragma pack(1)
typedef struct OtaPacketST
{    
    struct PacketHeaderST
    {
        char version[3];
        char type;
        char reserved[5];
        char moduletype;
        char dataLen[2];
    } header;
    char data[FRAME_DATA_REST_LENGTH];
	// uint8_t *data;
    char crc[2];
} OtaPacket;
#pragma pack()

#pragma pack(1)
typedef struct ConfigFrameST
{
    char head[3];
    OtaPacket packet;
    char tail[3];
} OtaFrame;
#pragma pack()

static OtaFrame s_frame_data_object;
static OTA_STATUS s_ota_cur_status = OTA_INIT;

rt_thread_t tcpConnectThread = RT_NULL;
rt_thread_t tcpReConnectThread = RT_NULL;
rt_thread_t tcpRecvThread= RT_NULL;
rt_thread_t tcpSendThread = RT_NULL;
rt_thread_t tcpHeartThread = RT_NULL;

static rt_sem_t heartBeatSem = RT_NULL;		//心跳包信号量S
static rt_sem_t tcpRecvSendSem = RT_NULL;	//收发同步信号量
static rt_sem_t tcpReConnectSem = RT_NULL;	//tcp重连

static rt_timer_t heartBeatTimer =RT_NULL;	//心跳包定时器

#define MODULE_TYPE  2
#define MD5_SALT "hkFy29Zsw8yN3HNV"		//MD5密文
#define PACK_MAX_SIZE  (5*1024)

extern unsigned int stm_mcu_id[3];

//devops.sciento.cn:8313    //线上
static char s_url[256] = "devops.sciento.cn";
static int s_port = 8313;

//内网测试 7013cd0336.imdo.co:36002
//static char s_url[256] = "7013cd0336.imdo.co";
//static int s_port = 36002;

static int s_sock = -1;
static char s_recvBuffer[PACK_MAX_SIZE+21]; 
static char eui[25] = {'\0'};
static int s_recvLenCnt = 0;    //接收长度计数

int otaFd = -1;

static int s_totalLength;
static int s_totalPackNum;
static char s_packMd5[33] = {'\0'};
static char s_newVersion[20] = {'\0'};
static int s_upgradeStatus;
static uint8_t heartNums = 1;
			
static int calc_md5(char*filename,char*dest)
{
    int i;
    int filelen = 0;
    int read_len;
    char temp[8]={0}; 
    char hexbuf[512]={0};
    unsigned char decrypt[16]={0};  
    unsigned char decrypt32[64]={0};
    tiny_md5_context md5;
    int fdf;

    fdf = open(filename,O_RDWR);
    if(fdf<0)
    {
        printf("%s not exist\n",filename);
        return -1;
    }

    tiny_md5_starts(&md5);  
    while(1)
    {
        read_len = read(fdf, hexbuf,sizeof(hexbuf)); 
        if (read_len <0) {  
			close(fdf);   
			LOG_I("calc_md5 len < 0,");
			return -1;
        }
        if(read_len==0){
			LOG_I("calc_md5 len = 0,");
            break;
        }
        filelen += read_len;
        tiny_md5_update(&md5,(unsigned char *)hexbuf,read_len); 
//		LOG_I("calc_md5 filelen = %d", filelen);
    }
	LOG_I("calc_md5 end filelen = %d", filelen);
    tiny_md5_finish(&md5,decrypt); 
    strcpy((char *)decrypt32,"");

    for(i=0;i<16;i++)
    {
        sprintf(temp,"%02x",decrypt[i]);
        strcat((char *)decrypt32,temp);
    }
    strcpy(dest,decrypt32);

    LOG_D("md5check:%s len=%d\n",dest,filelen);
    close(fdf);

    return filelen;
}

//MD5文件校验
static uint8_t md5CkeckHandler(void)
{
    char md5Ckeck[64] = " ";
    if(calc_md5(ST_OTA_FILE, md5Ckeck) > 0){
		LOG_D("Md5Handler = %s", md5Ckeck);
        if(strcmp(md5Ckeck, s_packMd5) == 0){
            return 1;
        }
    }
    return 0;
}

//升级信息清除
static void delOtaInfo(void)
{
	LOG_D("delet ota info");
    s_totalLength = 0;
    s_totalPackNum = 0;
    strcpy(s_packMd5, " ");
    strcpy(s_newVersion, " ");
    if (ef_set_env_blob("otaVersion", " ", Size_otaVersion) != EF_NO_ERR){
        LOG_E("delet otaVersion error");
    }
}

/*
*@brief: dev请求数据帧封包
*@param: data 数据
*@param: len 数据长度
*@param: type 帧类型
*/
static void devAskFrameEncode(const char *data, uint16_t len, OTA_FRAME_TYPE type)
{
    int ret = -1;
    int crclen = 0;
    uint16_t packcrc = 0;
	rt_memset((char *)&s_frame_data_object, 0, sizeof(OtaFrame));
    OtaFrame *p_frame_data = &s_frame_data_object;
	
    if(p_frame_data != NULL)
    {
        rt_memset(p_frame_data->head, FRAME_HEAR, FRAME_HEAR_LENGTH);
        rt_memcpy(p_frame_data->packet.header.version, FRAME_VERSION, FRAME_VERSION_LENGTH);
        p_frame_data->packet.header.type = type;
        rt_memset(p_frame_data->packet.header.reserved, 0x00, FRAME_RESERVE_LENGTH);
        p_frame_data->packet.header.moduletype = MODULE_TYPE;
        p_frame_data->packet.header.dataLen[0] = (len >>8) & 0xFF;
        p_frame_data->packet.header.dataLen[1] = len & 0xFF;
		// p_frame_data->packet.data = (uint8_t *)rt_malloc(len+1);
        rt_memcpy(p_frame_data->packet.data, data, len);
        crclen = FRAME_VERSION_LENGTH + FRAME_TYPE_LENGTH + FRAME_RESERVE_LENGTH + FRAME_DEVICE_TYPE_LENGTH + FRAME_DATA_LENGTH + len;
        packcrc = modbusCrc16((uint8_t *)(&p_frame_data->packet), crclen);
//        LOG_D("packcrc = %04X", packcrc);
        p_frame_data->packet.data[len] = (packcrc >> 8) & 0xFF;
        p_frame_data->packet.data[len+1] = packcrc & 0xFF;
        rt_memset(&p_frame_data->packet.data[len+2], FRAME_END, FRAME_END_LENGTH);
//		LOG_D("Frame [%d] sent data size: %d", type, len);

        if(s_sock != -1)
            ret = send(s_sock, p_frame_data, len+FRAME_EMPTY_LENGTH, 0);

        if(ret <= 0){
            LOG_E("dev [%d] frame send web failed !", type);
        }
        // rt_memset((char *)&s_frame_data_object, 0, sizeof(OtaFrame));
    }  
	p_frame_data = NULL;
}


//数据帧JSON解析
static int jsonDataCallback(cJSON *root, OTA_FRAME_TYPE frameType)
{
	if(frameType == OTA_REQUEST_CONNECT){
		cJSON *onlineFlag = cJSON_GetObjectItem(root, "onlineFlag");
		if (onlineFlag && (onlineFlag->type == cJSON_Number)) {
			if (onlineFlag->valueint == 1) {
                int len = 0;
                char version[20] = " ";
                len = ef_get_env_blob("otaVersion", version, Size_otaVersion, NULL);
                if(len){
					LOG_I("otaVersion：%s",version);
                    if(strcmp(version, " ") == 0) 
                        s_ota_cur_status = OTA_VERSION;
                    else
                        s_ota_cur_status = OTA_UPSUCCEED;
                }
                else{
                    s_ota_cur_status = OTA_CONNECT;
                }
                rt_sem_release(tcpRecvSendSem);
			}
            else{
                LOG_W("connect sciento tcp server error [%d]",OTA_REQUEST_CONNECT);
                s_ota_cur_status = OTA_CONNECT; //重新发连接请求
                rt_sem_release(tcpRecvSendSem);
                return 0;
            }
		}
	}
    else if(frameType == OTA_REQUEST_VERSION){
        cJSON *errorFlag = cJSON_GetObjectItem(root, "errorFlag");
        if(errorFlag && (errorFlag->type == cJSON_Number)){
            LOG_D("errorFlag = %d", errorFlag->valueint); //0正确， 1错误
            if(errorFlag->valueint ==0){
                cJSON *totalLength = cJSON_GetObjectItem(root, "totalLength");
                if(totalLength && (totalLength->type == cJSON_Number)) {
                    LOG_D("totalLength = %d",totalLength->valueint);
                    s_totalLength = totalLength->valueint;
                }
                cJSON *totalPackNum = cJSON_GetObjectItem(root, "totalPackNum");
                if(totalPackNum && (totalPackNum->type == cJSON_Number)) {
                    LOG_D("totalPackNum = %d", totalPackNum->valueint);
                    s_totalPackNum = totalPackNum->valueint;
                }
                cJSON *totalMd5 = cJSON_GetObjectItem(root, "md5");
                if(totalMd5 && (totalMd5->type == cJSON_String)){   
                    LOG_D("md5 = %s", totalMd5->valuestring);
                    rt_memcpy(s_packMd5, totalMd5->valuestring, strlen(totalMd5->valuestring));
					LOG_D("s_packMd5 = %s", s_packMd5);
                }
				cJSON *newVersion = cJSON_GetObjectItem(root, "newVersion");
                if(newVersion && (newVersion->type == cJSON_String)) {
                    LOG_D("newVersion = %s", newVersion->valuestring);
                    rt_memcpy(s_newVersion, newVersion->valuestring, strlen(newVersion->valuestring));
					LOG_D("s_newVersion = %s", s_newVersion);
                    if (ef_set_env_blob("otaVersion", s_newVersion, Size_otaVersion) == EF_NO_ERR){
                        LOG_D("set otaVersion success");
                    }
                }
                s_ota_cur_status = OTA_DATA;
                rt_sem_release(tcpRecvSendSem);
            }
            else{
                LOG_W("get version error [%d]", OTA_REQUEST_VERSION);
                s_ota_cur_status = OTA_END; //结束
                rt_sem_release(tcpRecvSendSem);
                return 0;
            }
        }    
    }
    else if(frameType == OTA_REQUEST_START) { //开始烧录
        cJSON *upgradeStatus = cJSON_GetObjectItem(root, "upgradeStatus");
        if(upgradeStatus && (upgradeStatus->type == cJSON_Number)){ 
            LOG_D("upgradeStatus = %d", upgradeStatus->valueint);
            s_upgradeStatus = upgradeStatus->valueint;  //2 同步状态成功
			if(upgradeStatus->valueint == 2){
				s_ota_cur_status = OTA_RREPLICATION;
				rt_sem_release(tcpRecvSendSem);
			}
//            s_ota_cur_status = OTA_UPSUCCEED;
//            rt_sem_release(tcpRecvSendSem);
        }
    }
    else if(frameType == OTA_REQUEST_UPGRADE_SUCCEED){	//烧录后
        cJSON *upgradeStatus = cJSON_GetObjectItem(root, "upgradeStatus");
        if(upgradeStatus && (upgradeStatus->type == cJSON_Number)){ 
            LOG_D("upgradeStatus = %d", upgradeStatus->valueint);
             s_upgradeStatus = upgradeStatus->valueint;  //3 升级成功
             s_ota_cur_status = OTA_END;
             rt_sem_release(tcpRecvSendSem);
        }
    }
    else if(frameType == OTA_RESPON_STATIC_UPGRADE){	//静态升级
        cJSON *reply = cJSON_GetObjectItem(root, "reply");
        if(reply && (reply->type == cJSON_Number)){
            LOG_D("reply = %d", reply->valueint); //0正确， 1错误
            if(reply->valueint == 0){
                s_ota_cur_status = OTA_END;
                rt_sem_release(tcpRecvSendSem);
                return 0;
            }
        }
        cJSON *staticUpgrade = cJSON_GetObjectItem(root, "staticUpgrade");
        if(staticUpgrade && (staticUpgrade->type == cJSON_Number)){ 
            LOG_D("staticUpgrade = %d", staticUpgrade->valueint);
            s_upgradeStatus = staticUpgrade->valueint;
            s_ota_cur_status = OTA_STATICUP;
            rt_sem_release(tcpRecvSendSem);
        }
    }
    else if(frameType == OTA_RESPON_REBOOT){
        cJSON *reboot = cJSON_GetObjectItem(root, "reboot");
        if(reboot && (reboot->type == cJSON_Number)){ 
            LOG_D("reboot = %d", reboot->valueint);
            if( reboot->valueint == 0 ){
                delOtaInfo();
                if(access(ST_OTA_FILE, 0) == 0){
					otaDelFile(ST_OTA_FILE);
                }
                rt_thread_mdelay(1500);
                NVIC_SystemReset();
            }
        }
    }
	else if(frameType == OTA_RESPON_HEART){
		cJSON *heartbeat = cJSON_GetObjectItem(root, "heartbeat");
		if(heartbeat && heartbeat->type == cJSON_Number){
			if(heartNums != 1)
				heartNums--;
			LOG_D("web respond heartBeat: %d",heartNums);
		}
	}
    else{

    }
    return 1;
}

/*
*@brief：web下发数据帧解析
*@param
*@return: 0 解析成功， 1 继续接收数据， -1 清空数据， -2 数据段处理错误 帧结构完整
*/
static int webResponFrameDencode(const char *data, int len)
{
    uint16_t inDataLen = len;
    OtaFrame *p_frame_data = (OtaFrame *)data;
    int res = 0;
    
    uint16_t data_len = p_frame_data->packet.header.dataLen[0]<<8 | p_frame_data->packet.header.dataLen[1];	//数据段长度
    uint16_t crc_len = FRAME_VERSION_LENGTH + FRAME_TYPE_LENGTH + FRAME_RESERVE_LENGTH + FRAME_DEVICE_TYPE_LENGTH + FRAME_DATA_LENGTH + data_len;//crc校验范围
    uint16_t frameLen = crc_len + FRAME_HEAR_LENGTH + FRAME_END_LENGTH + FRAME_CRC16_LENGTH;	//整帧数据长度

    LOG_D("indata = %d, framelen = %d", inDataLen, frameLen);
	
    //帧头
    if((uint8_t)p_frame_data->head[0] != 0xAC || (uint8_t)p_frame_data->head[1] != 0xAC || (uint8_t)p_frame_data->head[2] != 0xAC){
		LOG_E("frame head check error [%02X%02X%02X]",p_frame_data->head[0], p_frame_data->head[1], p_frame_data->head[2]);
        return -1;	//清空数据
    }
	
	//判断数据长度是否到 数据长度位
	if(inDataLen < FRAME_HEAR_LENGTH+FRAME_VERSION_LENGTH + FRAME_TYPE_LENGTH + FRAME_RESERVE_LENGTH + FRAME_DEVICE_TYPE_LENGTH){
		LOG_W("recv dataLen < FRAME_DATA_LENGTH");
		return 1; //返回继续接收数据
	}
	
	//判断数据段 长度
	if(data_len > FRAME_DATA_REST_LENGTH){
		LOG_E("Data segment > 5120");
		return -1; //清空数据
	}
	
	//判断数据长度是否等于 整帧数据长度
	if(inDataLen < FRAME_HEAR_LENGTH+FRAME_VERSION_LENGTH + FRAME_TYPE_LENGTH + FRAME_RESERVE_LENGTH + FRAME_DEVICE_TYPE_LENGTH + FRAME_DATA_LENGTH + data_len+FRAME_CRC16_LENGTH+FRAME_END_LENGTH){
		LOG_W("recv datalen < frame datalen");
		return 1; //返回继续接收
	}
	
    //帧类型
    OTA_FRAME_TYPE frameType = (OTA_FRAME_TYPE)(p_frame_data->packet.header.type);
    if(frameType != OTA_REQUEST_CONNECT && frameType != OTA_REQUEST_VERSION &&
       frameType != OTA_REQUEST_DATA && frameType != OTA_REQUEST_START &&
       frameType != OTA_REQUEST_UPGRADE_SUCCEED && frameType != OTA_RESPON_STATIC_UPGRADE &&
       frameType != OTA_RESPON_REBOOT && frameType != OTA_RESPON_HEART){
        LOG_E("frame type check error [%d]",frameType);
        return -1; //清空数据
    }
    //帧模组型号
//    uint8_t moduleType = p_frame_data->packet.header.moduletype;
//    if(moduleType != MODULE_TYPE){
//        LOG_E("frame moduletype check error [%d]", moduleType);
//        return -1; // 清空数据
//    }
    //crc校验
    uint16_t pack_crc = modbusCrc16((uint8_t *)(&p_frame_data->packet.header.version), crc_len);
    char *temp_crc = (char *)&pack_crc;
	
    if((temp_crc[0] != p_frame_data->packet.data[data_len+1]) || (temp_crc[1] != p_frame_data->packet.data[data_len])){
        LOG_E("pack_crc = %04X", pack_crc);
		LOG_E("temp_crc[0] = %02X, temp_crc[1] = %02X",temp_crc[0], temp_crc[1]);
		LOG_E("CRC0 = %02X, CRC1 = %02X", p_frame_data->packet.data[data_len], p_frame_data->packet.data[data_len+1]);
        LOG_E("frame crc check error");
        return -1; //清空数据
    }
    //帧尾
    if(((uint8_t)p_frame_data->packet.data[data_len+2] != 0xCA) || ((uint8_t)p_frame_data->packet.data[data_len+3] != 0xCA) || ((uint8_t)p_frame_data->packet.data[data_len+4] != 0xCA)){
        LOG_E("frame tail check error [%02X%02X%02X]", p_frame_data->packet.data[data_len+2], p_frame_data->packet.data[data_len+3], p_frame_data->packet.data[data_len+4]);
        return -1; //清空数据
    }

    //数据段处理, 按帧类型处理
    if(frameType != OTA_REQUEST_DATA){  
        char *json_data = NULL;
        cJSON *root = NULL;

        json_data = (char *)rt_malloc(data_len);
        if(json_data != NULL){   
            rt_memcpy(json_data, p_frame_data->packet.data, data_len);
            root = cJSON_Parse(json_data);
            if(root != NULL){
                //数据处理
                jsonDataCallback(root, frameType);
            }
            else{
                LOG_E("frame decode data encode json type failed");
                res = -2;
            }
        }
        else{
            LOG_E("frame dencode json_data malloc failed");
            res = -2;
        }
        if(root) cJSON_Delete(root);
        if(json_data){
            rt_free(json_data);
            json_data = NULL;
        }
    }
    else{   //循环获取数据包   = OTA_REQUEST_DATA
		int otaFdRet = -1;
        otaFd = otaOpenFile(ST_OTA_FILE, O_CREAT | O_RDWR | O_APPEND);
		otaFdRet = otaWriteFile(otaFd, p_frame_data->packet.data, data_len);
		otaFdRet = otaCloseFile(otaFd);
		LOG_D("ota.bin size = %d",otaGetFileSize(ST_OTA_FILE));
		if(otaFdRet == -1) res = -2;
        s_ota_cur_status = OTA_DATA;
        rt_sem_release(tcpRecvSendSem);
    }
    
    // recvLenCnt = 0; //解析通过hou
    return res;
}

/************* DEVICE ASK FRAME START ****************/

// 01 dev请求建立连接
static void devAskConnect(void)
{
    char *data = NULL;
    uint8_t i = 0;
	uint16_t datalen = 0;
    uint8_t md5data[16] = {0};
	char md51str[64] = {'\0'};
	char md52str[33] = {'\0'};

    tiny_md5((uint8_t *)eui, strlen(eui), md5data);
    for(i = 0; i<16; i++)
       rt_sprintf(&md51str[i*2], "%02x", md5data[i]);
	LOG_D("eui md5= %s", md51str);

	strcat(md51str, MD5_SALT);
	tiny_md5((uint8_t *)md51str, strlen(md51str), md5data);
	for(i=0; i<16; i++)
		rt_sprintf(&md52str[i*2], "%02x", md5data[i]);
    LOG_D("eui salt md5= %s", md52str);

    cJSON *root = cJSON_CreateObject();
    if (root != NULL) {
        cJSON_AddNumberToObject(root, "onlineFlag", 0);
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddStringToObject(root, "md5", md52str);
        cJSON_AddStringToObject(root, "curVersion", SOFTWARE_VERSION);
        cJSON_AddStringToObject(root, "productModel", PRODUCT_MODEL);
        cJSON_AddNumberToObject(root, "customFlag", 0);
        cJSON_AddNumberToObject(root, "reply", 1);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s", OTA_REQUEST_CONNECT, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_REQUEST_CONNECT);
			rt_free(data);
		}
		else
			LOG_E("devAskConnect data malloc failed");
		data = NULL;
    }
    else
        LOG_E("devAskConnect json root creat failed");
	
	if(root) cJSON_Delete(root);
	root = NULL;
}

//02 dev请求更新
static void devAskUpdate(void)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if (root != NULL) {
        cJSON_AddStringToObject(root, "oldVersion", SOFTWARE_VERSION);
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "reply", 1);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_REQUEST_VERSION, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_REQUEST_VERSION);
			rt_free(data);
		}
		else
			LOG_E("devAskUpdate data malloc failed");
        data = NULL;
    }
    else
        LOG_E("devAskUpdate json root creat failed");
	if(root) cJSON_Delete(root);
	root = NULL;
}

//03 dev请求分包下载数据
static void devAskDownData(uint16_t packnum)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if (root != NULL) {
        cJSON_AddNumberToObject(root, "index", packnum);
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "reply", 1);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_REQUEST_DATA, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_REQUEST_DATA);
			rt_free(data);
		}
		else
			LOG_E("devAskDownData data malloc failed");
		data = NULL;
	 
    }
    else
        LOG_E("devAskDownData packnum [%d] json root creat failed", packnum);
	if(root) cJSON_Delete(root);
	root = NULL;
}

//04 dev请求开始烧录
static void devAskStartUpgrade(uint8_t md5flag)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if (root != NULL) {
        if(md5flag){
            cJSON_AddNumberToObject(root, "checkFlag", md5flag);
            cJSON_AddNumberToObject(root, "upgradeStatus", 1);
        }
        else
            cJSON_AddNumberToObject(root, "checkFlag", md5flag);
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "reply", 1);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_REQUEST_START, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_REQUEST_START);
			rt_free(data);
		}
		else
			LOG_E("devAskUpgrade data malloc failed");
		data = NULL;
    }
    else
        LOG_E("devAskUpgrade json root creat failed");
	if(root) cJSON_Delete(root);
	root = NULL;
}

//05 dev请求更新升级状态
static void devAskAfterUpgradeUpdata(uint8_t upgradestatus)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if (root != NULL) {
        cJSON_AddStringToObject(root, "curVersion", SOFTWARE_VERSION);
        cJSON_AddNumberToObject(root, "upgradeStatus", upgradestatus);
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "reply", 1);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_REQUEST_UPGRADE_SUCCEED, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_REQUEST_UPGRADE_SUCCEED);
			rt_free(data);
		}
		else
			LOG_E("devAskAfterUpgradeUpdata data malloc failed");
        data = NULL;
    }
    else
        LOG_E("devAskAfterUpgradeUpdata json root creat failed");
	if(root) cJSON_Delete(root);
	root = NULL;
}

//06 dev应答web升级请求 静态升级
static void devResponWbeUpgrade(void)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if(root != NULL)
    {
        cJSON_AddNumberToObject(root, "staticUpgrade", 1); // 1 可升级
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "errorFlag", 0);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_RESPON_STATIC_UPGRADE, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_RESPON_STATIC_UPGRADE);
			rt_free(data);
		}
        else
			LOG_E("devResponWbeUpgrade data malloc failed");
        data = NULL;
    }
    else
        LOG_E("devResponWbeUpgrade json root creat failed");
	if(root) cJSON_Delete(root);
	root = NULL;
}

// 07 dev响应web重启

// 08 dev web双向心跳帧
static void devUpHeart(uint8_t heartnum)
{
    char *data = NULL;
    uint16_t datalen = 0;
    cJSON *root = cJSON_CreateObject();
    if(root != NULL)
    {
        cJSON_AddStringToObject(root, "eui", eui);
        cJSON_AddNumberToObject(root, "heartbeat", heartnum);
        data = cJSON_PrintUnformatted(root);
		if(data){
			LOG_D("[%d] josn data:%s",OTA_RESPON_HEART, data);
			datalen = (uint16_t)strlen(data);
			devAskFrameEncode(data, datalen, OTA_RESPON_HEART);
			free(data);
		}
        else
			LOG_E("devResponWebHeart data malloc failed");
        data = NULL;
    }
    else
        LOG_E("devResponWebHeart json root creat failed");
	if(root) cJSON_Delete(root);
	root = NULL;
	
}

/************* DEVICE ASK FRAME END ******************/


/****************************** OTA TASK START ********************************/
//tcp数据发送任务
static void tcpOtaSendTask(void *parameter)
{
	LOG_D("start tcpOtaSendTask,ota_status:%d",s_ota_cur_status);
    rt_err_t result = RT_ERROR;
    static int packNum = 0;
    char version[Size_otaVersion] = {'\0'};
    uint8_t res = 0;
    int len = -1;
	int bakres = -1;
    while(1)
    {
        result = rt_sem_take(tcpRecvSendSem, RT_WAITING_FOREVER);
        if(result == RT_EOK){
            switch (s_ota_cur_status)
            {
            case OTA_CONNECT:
                rt_thread_mdelay(3000);
                devAskConnect();
                // s_ota_cur_status = OTA_INIT;
                break;
            case OTA_VERSION:
                devAskUpdate();
                break;
            case OTA_DATA:
                if(packNum == 0){
					if(access(ST_OTA_FILE, 0) == 0){
						otaDelFile(ST_OTA_FILE);
					}
				}
                if(packNum < s_totalPackNum){
					rt_thread_mdelay(1000);
                    devAskDownData(packNum); 
                    packNum++;
                }
                else{
					s_ota_cur_status = OTA_START;
                    rt_sem_release(tcpRecvSendSem);
                    packNum = 0;
                }     
                break;
			case OTA_START:
				res = md5CkeckHandler();
				devAskStartUpgrade(res);
				if(res == 0){
					s_ota_cur_status = OTA_END;
                    rt_sem_release(tcpRecvSendSem);
				}
				break;
            case OTA_RREPLICATION:     //烧录结束设备会重启，重新连接TCp //检验失败 平台断开tcp， 不回复
				while(bakres < 0){
					if(access(ST_BAK_FILE, 0) == 0){
						LOG_D("delet %s file",ST_BAK_FILE);
						otaDelFile(ST_BAK_FILE);
					}
					bakres = copyApptoBak();	//备份当前固件到备份区
				}
				if(checkBakCrc32() < 0){
					LOG_E("bak.bin checkcrc fail");
					closesocket(s_sock);	//需要可考虑重新连接tcp
                    s_sock = -1;
					s_ota_cur_status = OTA_END;
                    rt_sem_release(tcpRecvSendSem);
				}
				else{
					rt_thread_mdelay(3000);
					rt_pin_write(GET_PIN(A, 15), PIN_LOW);	//关闭4G模组
					askReprogramAPP(OTA_START_UPGRADE);	//进入bootloader
				}
             
                break;
            case OTA_UPSUCCEED:
                len = ef_get_env_blob("otaVersion", version, Size_otaVersion, NULL);
                if(len){
                   if((strcmp(version, SOFTWARE_VERSION)) == 0){
                        devAskAfterUpgradeUpdata(3);    //升级成
                   }
                   else{
                        LOG_E("OTA upgrade fail");
						devAskAfterUpgradeUpdata(0);    //升级失败 平台断开tcp ，不回复
						s_ota_cur_status = OTA_END;
						rt_sem_release(tcpRecvSendSem);
                   }
                }
                else{
                    LOG_E("OTA get otaVersion fail, retrying...");
                    rt_thread_mdelay(3000);
                    s_ota_cur_status = OTA_UPSUCCEED;
                    rt_sem_release(tcpRecvSendSem);
                }
                break;
            case OTA_STATICUP:
                devResponWbeUpgrade();
                rt_thread_mdelay(3000);
                s_ota_cur_status = OTA_VERSION;
				packNum = 0;
				delOtaInfo();    //删除升级信息
                rt_sem_release(tcpRecvSendSem);
                break;
//            case OTA_REBOOT:
//                break;
//            case OTA_HEART:
//                break;
            case OTA_END:
				if(access(ST_OTA_FILE, 0) == 0){
					LOG_D("delet ota file");
					otaDelFile(ST_OTA_FILE);
                }
                delOtaInfo();    //删除升级信息
				LOG_D("OTA END");
                break;
            default:
                break;
            }
        }
    }
}


//tcp数据接收处理任务
static void tcpOtaRecvTask(void *parameter)
{
	LOG_D("start tcpOtaRecvTask");
    int ret = -1;
    int len = -1;
    int res = 0;
 
    struct timeval timeout;
    fd_set readfds;
//	struct timeval recvtimeout = {5, 0};
//  setsockopt(s_sock, SOL_SOCKET, SO_RCVTIMEO, &recvtimeout, sizeof(recvtimeout));

	timeout.tv_sec = 3;
	timeout.tv_usec = 0;
	while(1)
	{
		while(s_sock != -1)
		{
			FD_ZERO(&readfds);
			FD_SET(s_sock, &readfds);

			ret = select(s_sock+1, &readfds, NULL, NULL, &timeout);
			if(ret > 0)
			{
				if(FD_ISSET(s_sock, &readfds))
				{
					while(1)
					{
						//接受到数据
						len = recv(s_sock, &s_recvBuffer[s_recvLenCnt], 5140, 0);    
						if(len > 0)
						{
							//考虑数据的接收和解析分开两个线程
							s_recvLenCnt += len;
							res = webResponFrameDencode(s_recvBuffer, s_recvLenCnt);
							if(res == 0){
	//                            LOG_D("frame dencode ok");
								s_recvLenCnt = 0;
								break;
							}
							else if(res == -1){
								LOG_E("frame check error");
								s_recvLenCnt = 0;
								break;
							}
							else if(res == 1){	//继续接收
								LOG_W("frame continue recv");
							}
							else{
								LOG_E("frame recv finish, but dataHandler error");
								s_recvLenCnt = 0;
								break;
							}
						}  
						else
						{
							LOG_E("OTA recv error ! recv option return [%d]",len);
							closesocket(s_sock);
							s_sock = -1;
							rt_thread_mdelay(1500);
							// rt_memset(tempBuffer, 0, PACK_MAX_SIZE);
							rt_sem_release(tcpReConnectSem);
							break;
						}                  
					}
					rt_memset(s_recvBuffer, 0, sizeof(s_recvBuffer));
				}
			}
			
		}
		
	}
}

//tcp心跳包任务
static void tcpOtaHeartTask(void *paramter)
{
    rt_err_t result = RT_ERROR;
	while(1){

		result = rt_sem_take(heartBeatSem, RT_WAITING_FOREVER);
		if(result == RT_EOK && s_ota_cur_status != OTA_DATA && s_sock != -1)
		{
			if(heartNums > 3){
				closesocket(s_sock);
				s_sock = -1;
				heartNums = 1;
				rt_sem_release(tcpReConnectSem);
			}
			else
			{
				devUpHeart(heartNums);
				heartNums++;
			}
		}
	}
}

//tcp通信连接任务
static void tcpOtaConnectTask(void *parameter)
{
    struct hostent *host = RT_NULL;
    struct sockaddr_in server_addr;
	// struct timeval recvtimeout = {7200, 0};
    while(1)
    {
        rt_thread_mdelay(3000);
        /* 通过函数入口参数url获得host地址（如果是域名，会做域名解析） */
        host = gethostbyname(s_url);
        if (host == RT_NULL)
        {
            LOG_E("Get host by name failed!");
            continue;
        }

        /* 创建一个socket，类型是SOCKET_STREAM，TCP类型 */
        if ((s_sock = socket(AF_AT, SOCK_STREAM, 0)) == -1) 
        {
            /* 创建socket失败 */
            LOG_E("Create socket error");
            closesocket(s_sock);
            s_sock = -1;
            continue;
        }

        /* 初始化预连接的服务端地址 */
        server_addr.sin_family = AF_AT;
        server_addr.sin_port = htons(s_port);
        server_addr.sin_addr = *((struct in_addr *)host->h_addr);
        rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

        /* 连接到服务端 */
        if (connect(s_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
        {
            /* 连接失败 */
            LOG_E("TCP Connect fail!");
            closesocket(s_sock);
            s_sock = -1;
            continue;
        }
        
        // setsockopt(s_sock,SOL_SOCKET,SO_KEEPALIVE, &recvtimeout, sizeof(recvtimeout));

        LOG_D("TCP Connect success!");
        s_ota_cur_status = OTA_CONNECT;
        // int bufsize = 0;
        // getsockopt(SOL_SOCKET, SOL_SOCKET, SO_RCVBUF, &bufsize, (socklen_t *)sizeof(bufsize));
        // LOG_D("recvbuf size = %d", bufsize);

		tcpRecvThread = rt_thread_create("OtaRecv",
						tcpOtaRecvTask, RT_NULL,
						4096, 12, 20);
		if (tcpRecvThread != RT_NULL)
			rt_thread_startup(tcpRecvThread);
		else
			LOG_E("start tcpRecvThread fail");
       
		tcpSendThread = rt_thread_create("OtaSend",
						tcpOtaSendTask, RT_NULL,
						4096, 12, 20);
		if (tcpSendThread != RT_NULL)
			rt_thread_startup(tcpSendThread);
		else
			LOG_E("start tcpSendThread fail");
 
        
		tcpHeartThread = rt_thread_create("OtaHeart",
						tcpOtaHeartTask, RT_NULL,
						2048, 13, 20);
		if (tcpHeartThread != RT_NULL)
			rt_thread_startup(tcpHeartThread);
		else
			LOG_E("start tcpHeartThread fail");
        
        rt_sem_release(tcpRecvSendSem);
		break;
    }

}

//tcp重连
static void tcpOtaReConnectTask(void *parameter)
{
    struct hostent *host = RT_NULL;
    struct sockaddr_in server_addr;
	rt_err_t result = RT_ERROR;
	// struct timeval recvtimeout = {7200, 0};
    while(1)
    {
		result = rt_sem_take(tcpReConnectSem, RT_WAITING_FOREVER);
		if(result == RT_EOK){
			while(1){
				rt_thread_mdelay(13000);
				/* 通过函数入口参数url获得host地址（如果是域名，会做域名解析） */
				host = gethostbyname(s_url);
				if (host == RT_NULL){
					LOG_E("Get host by name failed!");
					continue;
				}

				/* 创建一个socket，类型是SOCKET_STREAM，TCP类型 */
				if ((s_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
					/* 创建socket失败 */
					LOG_E("Create socket error");
					closesocket(s_sock);
					s_sock = -1;
					continue;
				}

				/* 初始化预连接的服务端地址 */
				server_addr.sin_family = AF_INET;
				server_addr.sin_port = htons(s_port);
				server_addr.sin_addr = *((struct in_addr *)host->h_addr);
				rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

				/* 连接到服务端 */
				if (connect(s_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){
					/* 连接失败 */
					LOG_E("TCP ReConnect fail!");
					closesocket(s_sock);
					s_sock = -1;
					continue;
				}
				
				// setsockopt(s_sock,SOL_SOCKET,SO_KEEPALIVE, &recvtimeout, sizeof(recvtimeout));

				LOG_D("TCP ReConnect success!");
				s_ota_cur_status = OTA_CONNECT;
			
				rt_thread_mdelay(2000);
				rt_sem_release(tcpRecvSendSem);
				
				break;
			}
		}
	}
}

//心跳包定时回调
static void tcpHeartTimeoutCallback(void *paramter)
{
    rt_sem_release(heartBeatSem);
}

/********************** OTA TASK END *********************************/

int tcpOtaTaskInit(void)
{
//	delOtaInfo();
    rt_memset(s_recvBuffer, 0, FRAME_DATA_REST_LENGTH);
    rt_memset((char *)&s_frame_data_object, 0, sizeof(OtaFrame));
    rt_sprintf(eui, "%08x%08x%08x",stm_mcu_id[0],stm_mcu_id[1],stm_mcu_id[2]); 

	heartBeatSem = rt_sem_create("tcpHeartSem", 0, RT_IPC_FLAG_FIFO);
    if (heartBeatSem == RT_NULL)
        LOG_E("creat heartBeatSem fail");

    tcpRecvSendSem = rt_sem_create("tcpRecvSendSem", 0, RT_IPC_FLAG_FIFO);
    if (tcpRecvSendSem == RT_NULL)
        LOG_E("creat tcpRecvSendSem fail");
	
	tcpReConnectSem = rt_sem_create("tcpReConnectSem", 0, RT_IPC_FLAG_FIFO);
    if (tcpReConnectSem == RT_NULL)
        LOG_E("creat tcpReConnectSem fail");
        

    heartBeatTimer = rt_timer_create("tcpHeartTimer", tcpHeartTimeoutCallback, 
								RT_NULL, 290* RT_TICK_PER_SECOND,
                                RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
    if (heartBeatTimer != RT_NULL)
        rt_timer_start(heartBeatTimer);
    else
        LOG_E("start up heartBeatTimer fail!");

	tcpConnectThread = rt_thread_create("OtaConnect",
                       tcpOtaConnectTask, RT_NULL,
                       2048, 12, 20);
	if (tcpConnectThread != RT_NULL)
        rt_thread_startup(tcpConnectThread);
    else
        LOG_E("start tcpConnectThread fail");
	
	tcpReConnectThread = rt_thread_create("OtaReConnect",
                       tcpOtaReConnectTask, RT_NULL,
                       2048, 11, 20);
	if (tcpReConnectThread != RT_NULL)
        rt_thread_startup(tcpReConnectThread);
    else
        LOG_E("start tcpReConnectThread fail");
	
	
	return RT_EOK;
}


static void stReconnectOtaTcp(void)
{
//    int ret = 0;
//    ret = closesocket(s_sock);
//    s_sock = -1;
    rt_thread_resume(tcpConnectThread);
//    LOG_D("close ota socket [%d]", ret);
}
MSH_CMD_EXPORT(stReconnectOtaTcp, stReconnectOtaTcp);

static void stCloseOtaTcp(void)
{
    int ret = 0;
    ret = closesocket(s_sock);
    s_sock = -1;
	rt_thread_delete(tcpHeartThread);
    rt_thread_delete(tcpRecvThread);
    rt_thread_delete(tcpSendThread);
//    rt_thread_resume(tcpConnectThread);
    LOG_D("close ota socket [%d]", ret);
}
MSH_CMD_EXPORT(stCloseOtaTcp, stCloseOtaTcp);

static int FileMd5(int argc, char **argv)
{
    int filelen;
    char md5_str[64]={0};
	if(!rt_strcmp(argv[1], "ota"))
	{
		filelen = calc_md5(ST_OTA_FILE,md5_str);
	}
	if(!rt_strcmp(argv[1], "bak"))
	{
		filelen = calc_md5(ST_BAK_FILE,md5_str);
	}   
    if(filelen<0)
    {
        printf("calc_md5 fail\n");
        return -1;
    }

 return 0;
}
MSH_CMD_EXPORT(FileMd5, FileMd5);

static void intoOtaBoot(void)
{
	askReprogramAPP(OTA_START_UPGRADE);	//进入bootloader
}
MSH_CMD_EXPORT(intoOtaBoot, intoOtaBoot);


//#ifdef RT_USING_FINSH
//MSH_CMD_EXPORT_ALIAS(tcpclient, tcpclient,
//    Start a tcp client. Help: tcpclient --help);
//#endif
